#pragma once

#include "slice.h"

namespace sfc::slice {

template <class T>
struct Iter {
  using Item = T&;

  T* _ptr;
  T* _end;

 public:
  [[sfc_inline]] Iter(T* ptr, usize len) noexcept : _ptr{ptr}, _end{_ptr + len} {}

  [[sfc_inline]] auto len() const -> usize {
    const auto res = _end - _ptr;
    return res >= 0 ? static_cast<usize>(res) : 0U;
  }

  [[sfc_inline]] auto next() -> option::Option<T&> {
    if (_ptr >= _end) return {};
    return *(_ptr++);
  }

  [[sfc_inline]] auto next_back() -> option::Option<T&> {
    if (_ptr >= _end) return {};
    return *(--_end);
  }

  [[sfc_inline]] auto operator->() -> iter::Iter<Iter>* {
    return static_cast<iter::Iter<Iter>*>(this);
  }
};

template <class T>
auto Slice<T>::iter() const -> Iter {
  return {_ptr, _len};
}

template <class T>
auto Slice<T>::iter_mut() -> IterMut {
  return {_ptr, _len};
}

template <class T>
struct Windows {
  using Item = Slice<T>;

  Slice<T> _buf;
  usize _len;

 public:
  [[sfc_inline]] operator bool() const {
    return _buf._len >= _len;
  }

  [[sfc_inline]] auto next() -> option::Option<Item> {
    if (_buf._len < _len) return {};

    auto res = Item{_buf._ptr, _len};
    _buf._ptr += 1;
    _buf._len -= 1;
    return res;
  }

  [[sfc_inline]] auto next_back() -> option::Option<Item> {
    if (_buf._len < _len) return {};

    auto res = Item{_buf._ptr + _buf._len - _len, _len};
    _buf._len -= 1;
    return res;
  }

  [[sfc_inline]] auto operator->() -> iter::Iter<Windows>* {
    return static_cast<iter::Iter<Windows>*>(this);
  }
};

template <class T>
auto Slice<T>::windows(usize wlen) const {
  return Windows<const T>{*this, wlen};
}

template <class T>
auto Slice<T>::windows_mut(usize wlen) {
  return Windows<T>{*this, wlen};
}

template <class T>
struct Truncks {
  Slice<T> _buf;
  usize _len;

 public:
  [[sfc_inline]] operator bool() const {
    return _buf._len >= _len;
  }

  [[sfc_inline]] auto next() -> option::Option<Slice<T>> {
    if (_buf._len < _len) return {};

    auto res = Slice<T>{_buf._ptr, _len};
    _buf._ptr += _len;
    _buf._len -= _len;
    return res;
  }

  auto operator->() -> iter::Iter<Truncks>* {
    return static_cast<iter::Iter<Truncks>*>(this);
  }
};

template <class T>
auto Slice<T>::truncks(usize wlen) const {
  return Truncks<const T>{*this, wlen};
}

template <class T>
auto Slice<T>::truncks_mut(usize wlen) {
  return Truncks<T>{*this, wlen};
}

}  // namespace sfc::slice
